#pragma once
#include <zCoreConfig.hpp>
#include <string>
#include "Log.hpp"
#include "IOObject.hpp"
#include "../Math/Math.hpp"

namespace zzz {
#ifdef ZZZ_STLSTRING
class STLString : public std::string {
public:
  typedef std::string::iterator iterator;
  typedef std::string::reverse_iterator reverse_iterator;
  typedef std::string::const_iterator const_iterator;
  typedef std::string::const_reverse_iterator const_reverse_iterator;
  typedef std::string::reference reference;
  typedef std::string::const_reference const_reference;

  STLString():std::string(){}
  STLString(const string& str){assign(str);}
  STLString(const string& str, size_t pos, size_t n = std::string::npos) {assign(str, pos, n);}
  STLString(const char* s, size_t n){assign(s, s);}
  STLString(const char* s){assign(s);}
  STLString(size_t n, char c){assign(n, c);}
  template<class InputIterator>
  STLString(InputIterator begin, InputIterator end) {
    assign(begin, end);
  }
  STLString(const STLString& x){assign(x);}

  // iterators
  STLString& operator=(const std::string& x) {return assign(x);}
  STLString& operator=(const STLString& x) {return assign(x);}
  STLString& operator=(const char* s) {return assign(s);}
  STLString& operator=(char c) {clear(); push_back(c);}
  using std::string::begin;
  using std::string::end;
  using std::string::rbegin;
  using std::string::rend;
 
  // capacity
  using std::string::size;
  using std::string::length;
  using std::string::max_size;
  using std::string::resize;
  using std::string::capacity;
  using std::string::clear;
  using std::string::empty;
  void reserve(size_type n) {
    try {
      std::string::reserve(n);
    } catch(std::bad_alloc) {
      ZLOGF<<"STLString: Failed to allocate memory!";
    } catch(std::length_error) {
      ZLOGF<<"STLString: Length of string is longer than "<<ZVAR(max_size());
    }
  }

  // element access
  reference operator[](size_type i) {return at(i);}
  const_reference operator[](size_type i) const {return at(i);}
  reference at(size_type i) {
    ZRCHECK_LT(i, size());
    return std::string::at(i);
  }
  const_reference at(size_type i) const {
    ZRCHECK_LT(i, size());
    return std::string::at(i);
  }

  // modifiers
  STLString& assign(const string& str) {assign(str.begin(), str.end()); return *this;}
  STLString& assign(const STLString& str) {return assign(static_cast<const std::string&>(str));}
  STLString& assign(const string& str, size_t pos, size_t n) {
    if (n == std::string::npos)
      n = str.size() - pos;
    else
      n = Min(str.size() - pos, n);
    assign(str.begin() + pos, str.begin() + pos + n);
    return *this;
  }
  STLString& assign(const char* s, size_t n) {assign(s, s + n); return *this;}
  STLString& assign(const char* s) {return assign(s, strlen(s));}
  template <class InputIterator>
  void assign(InputIterator first, InputIterator last) {
    try {
      std::string::assign(first, last);
    } catch(std::bad_alloc) {
      ZLOGF<<"STLString: Failed to allocate memory!";
    } catch(std::length_error) {
      ZLOGF<<"STLString: Length of string is longer than "<<ZVAR(max_size());
    }
  }
  void assign(size_type n, char u) {
    try {
      std::string::assign(n, u);
    } catch(std::bad_alloc) {
      ZLOGF<<"STLString: Failed to allocate memory!";
    } catch(std::length_error) {
      ZLOGF<<"STLString: Length of string is longer than "<<ZVAR(max_size());
    }
  }

  string& operator+=(const string& str) {return append(str);}
  string& operator+=(const char* s) {return append(s);}
  string& operator+=(char c) {push_back(c); return *this;}
  string& append(const string& str) {return append(str.begin(), str.end());}
  string& append(const string& str, size_t pos, size_t n) {
    if (n == std::string::npos)
      n = str.size() - pos;
    else
      n = Min(str.size() - pos, n);
    return append(str.begin() + pos, str.begin() + pos + n);
  }
  string& append(const char* s, size_t n) {return append(s, s+n);}
  string& append(const char* s){return append(s, s+strlen(s));}
  string& append(size_t n, char c) {
    try {
      return std::string::append(n, c);
    } catch(std::bad_alloc) {
      ZLOGF<<"STLString: Failed to allocate memory!";
    } catch(std::length_error) {
      ZLOGF<<"STLString: Length of string is longer than "<<ZVAR(max_size());
    }
  }
  template <class InputIterator>
  string& append(InputIterator first, InputIterator last) {
    try {
      return std::string::append(first, last);
    } catch(std::bad_alloc) {
      ZLOGF<<"STLString: Failed to allocate memory!";
    } catch(std::length_error) {
      ZLOGF<<"STLString: Length of string is longer than "<<ZVAR(max_size());
    }
  }

  void push_back(char x) {append(1, x);}
  string& insert(size_t pos1, const string& str) {insert(begin()+pos1, str.begin(), str.end()); return *this;}
  string& insert(size_t pos1, const string& str, size_t pos2, size_t n) {
    if (n == std::string::npos)
      n = str.size() - pos2;
    else
      n = Min(str.size() - pos2, n);
    insert(begin()+pos1, str.begin()+pos2, str.begin()+pos2+n);
    return *this;
  }
  string& insert(size_t pos1, const char* s, size_t n) {insert(begin()+pos1, s, s+n);}
  string& insert(size_t pos1, const char* s) {return insert(pos1, s, strlen(s));}
  string& insert(size_t pos1, size_t n, char c) {return insert(pos1, n, c);}
  iterator insert(iterator p, char c) {
    try {
      return std::string::insert(p, c);
    } catch(std::bad_alloc) {
      ZLOGF<<"STLString: Failed to allocate memory!";
    } catch(std::length_error) {
      ZLOGF<<"STLString: Length of string is longer than "<<ZVAR(max_size());
    }
  }
  void insert(iterator p, size_t n, char c) {
    try {
      std::string::insert(p, n, c);
    } catch(std::bad_alloc) {
      ZLOGF<<"STLString: Failed to allocate memory!";
    } catch(std::length_error) {
      ZLOGF<<"STLString: Length of string is longer than "<<ZVAR(max_size());
    }
  }
  template<class InputIterator>
  void insert(iterator p, InputIterator first, InputIterator last) {
    try {
      std::string::insert(p, first, last);
    } catch(std::bad_alloc) {
      ZLOGF<<"STLString: Failed to allocate memory!";
    } catch(std::length_error) {
      ZLOGF<<"STLString: Length of string is longer than "<<ZVAR(max_size());
    }
  }
  using std::string::erase;
  using std::string::replace;
  using std::string::swap;

  // String operations
  using std::string::c_str;
  using std::string::data;
  using std::string::get_allocator;
  using std::string::copy;
  using std::string::find;
  using std::string::rfind;
  using std::string::find_first_of;
  using std::string::find_last_of;
  using std::string::find_first_not_of;
  using std::string::find_last_not_of;
  using std::string::substr;
  using std::string::compare;

  // Special
  char* Data() {return &(at(0));}
  const char* Data() const {return &(at(0));}
};
#else
class STLString : public std::std::string {
public:
public:
  typedef std::std::string::iterator iterator;
  typedef std::std::string::reverse_iterator reverse_iterator;
  typedef std::std::string::const_iterator const_iterator;
  typedef std::std::string::const_reverse_iterator const_reverse_iterator;
  typedef typename std::std::string::reference reference;
  typedef typename std::std::string::const_reference const_reference;

  STLString():std::string(){}
  STLString(const string& str){assign(str);}
  STLString(const string& str, size_t pos, size_t n = std::string::npos) {assign(str, pos, n);}
  STLString(const char* s, size_t n){assign(s, s);}
  STLString(const char* s){assign(s);}
  STLString(size_t n, char c){assign(n, c);}
  template<class InputIterator>
  STLString(InputIterator begin, InputIterator end) {
    assgin(begin, end);
  }
  STLString(const STLString& x){assign(x);}

  // iterators
  using std::string::operator=;
  using std::string::begin;
  using std::string::end;
  using std::string::rbegin;
  using std::string::rend;
 
  // capacity
  using std::string::size;
  using std::string::length;
  using std::string::max_size;
  using std::string::resize;
  using std::string::capacity;
  using std::string::clear;
  using std::string::empty;
  using std::string::reserve(size_type n);

  // element access
  using std::string::operator[];
  using std::string::at;

  // modifiers
  using std::string::assign;
  using std::string::append;
  using std::string::push_back;
  using std::string::insert;
  using std::string::erase;
  using std::string::replace;
  using std::string::swap;

  // String operations
  using std::string::c_str;
  using std::string::data;
  using std::string::get_allocator;
  using std::string::copy;
  using std::string::find;
  using std::string::rfind;
  using std::string::find_first_of;
  using std::string::find_last_of;
  using std::string::find_first_not_of;
  using std::string::find_last_not_of;
  using std::string::substr;
  using std::string::compare;

  // Special
  char* Data() {return std::string::data();}
  const char* Data() const {return std::string::data();}
};
#endif
template<>
class IOObject<STLString>
{
public:
  static void WriteFileB(FILE *fp, const STLString &src)
  {
    zuint64 len = src.size();
    IOObj::WriteFileB(fp, len);
    if (len != 0) IOObj::WriteFileB(fp, src.data(), len);
  }
  static void ReadFileB(FILE *fp, STLString &dst)
  {
    zuint64 len;
    IOObj::ReadFileB(fp, len);
    if (len != 0) {
      dst.resize(len);
      IOObj::ReadFileB(fp, dst.Data(), len);
    }
    else
      dst.clear();
  }
  static void WriteFileR(RecordFile &rf, const zint32 label, const STLString &src)
  {
    zuint64 len = src.size();
    IOObj::WriteFileR(rf, label, len);
    if (len != 0) IOObj::WriteFileR(rf, src.Data(), src.size());
  }
  static void ReadFileR(RecordFile &rf, const zint32 label, STLString &dst)
  {
    if (!rf.LabelExist(label)) {
      dst.clear();
      return;
    }
    zuint64 len;
    IOObj::ReadFileR(rf, label, len);
    if (len != 0) {
      dst.resize(len);
      IOObj::ReadFileR(rf, dst.Data(), dst.size());
    }
    else
      dst.clear();
  }
};

}
